package com.chinafree.permission.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.chinafree.mapper.PermissionMapper;
import com.chinafree.mapper.RoleMapper;
import com.chinafree.mapper.RolePermissionMapper;
import com.chinafree.mapper.UserRoleMapper;
import com.chinafree.permission.converter.RoleConverter;
import com.chinafree.permission.converter.RolePermissionConverter;
import com.chinafree.permission.model.bo.RoleBO;
import com.chinafree.permission.model.bo.RoleDepartUserEditBO;
import com.chinafree.permission.model.bo.RolePermissionEditBO;
import com.chinafree.permission.model.dto.PermissionDTO;
import com.chinafree.permission.model.dto.RoleDTO;
import com.chinafree.permission.model.dto.RoleInfoDTO;
import com.chinafree.permission.model.enumeration.ColumnEnum;
import com.chinafree.permission.model.enumeration.PermissionTypeEnum;
import com.chinafree.permission.model.enumeration.RedisKeyEnum;
import com.chinafree.permission.model.param.RoleParam;
import com.chinafree.permission.model.po.PermissionPO;
import com.chinafree.permission.model.po.RolePO;
import com.chinafree.permission.model.po.RolePermissionPO;
import com.chinafree.permission.model.po.UserRolePO;
import com.chinafree.permission.service.PerRoleService;
import com.chinafree.permission.service.RolePermissionService;
import com.chinafree.permission.service.UserRoleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @description: TODO
 * @author: longzix
 * @modified By: longzix
 * @date: Created in 2021/7/23 15:53
 * @version:v1.0
 */
@Slf4j
@Service
public class RolePermissionServiceImpl implements RolePermissionService {


    @Resource
    private RoleMapper roleMapper;

    @Resource
    private PermissionMapper permissionMapper;

    @Resource
    private UserRoleMapper userRoleMapper;

    @Resource
    private RolePermissionMapper rolePermissionMapper;

    @Autowired
    private UserRoleService userRoleService;

    @Autowired
    private PerRoleService perRoleService;

    @Autowired
    private RedisTemplate redisTemplate;


    @Resource
    private ThreadPoolTaskExecutor taskExecutor;

    /**
     * @description: 获取角色列表信息
     * @author longzix
     * @date 2021/7/25 20:00
     * @return java.util.List<com.xinheyun.operation.auth.model.vo.RoleVo>
     */
    @Override
    public Optional<List<RoleDTO>> getRoleList() {
        QueryWrapper<RolePO> wrapper = new QueryWrapper<>();
        wrapper.eq(ColumnEnum.IS_DELETE.getValue(), 0);
        List<RolePO> rolePOS = roleMapper.selectList(wrapper);
        if (CollectionUtils.isEmpty(rolePOS)) {
            return Optional.empty();
        }
        List<RoleDTO> roleDTOS = RolePermissionConverter.INSTANCE.rolePOListToRoleDTOList(rolePOS);
        return Optional.of(roleDTOS);
    }

    /**
     * @description: 获取权限列表按照权限类型划分
     * @author longzix
     * @date 2021/7/25 20:55
     * @return java.util.Map<com.xinheyun.operation.auth.model.enumeration.PermissionTypeEnum,java.util.List<com.xinheyun.operation.auth.dto.PermissionDTO>>
     */
    @Override
    public Optional<Map<PermissionTypeEnum, List<PermissionDTO>>> getPermissionList() {
        List<PermissionPO> permissionPOS = permissionMapper.selectPermissionList();
        if (CollectionUtils.isEmpty(permissionPOS)) {
            return Optional.empty();
        }
        //根据order字段排序
        List<PermissionPO> sortList = permissionPOS.stream().sorted(Comparator.comparingInt(PermissionPO::getOrder)).collect(Collectors.toList());
        Map<PermissionTypeEnum, List<PermissionDTO>> resultMap = new EnumMap<>(PermissionTypeEnum.class);//
        typePermissionMap(resultMap, sortList);//将permissionPos封装type对应permission的Map
        return Optional.of(resultMap);
    }

    private void typePermissionMap(Map<PermissionTypeEnum, List<PermissionDTO>> resultMap, List<PermissionPO> permissionPOS) {
        PermissionTypeEnum[] values = PermissionTypeEnum.values();//获取所有的枚举类型
        for (PermissionTypeEnum value : values) {
            List<PermissionPO> permissionPOList = new ArrayList<>();
            permissionPOS.forEach(permission -> {
                if (value.getTypeId().equals(permission.getType())) {
                    permissionPOList.add(permission);
                }
            });
            resultMap.put(value, RolePermissionConverter.INSTANCE.permissionPOListToPermissionDTOList(permissionPOList));
        }
    }

    /* *
     * @description: 根据角色id 获取角色的基本信息，以及对应的用户列表，部门列表，权限列表
     * @param roleId
     * @author longzix
     * @date 2021/7/25 21:46
     * @return com.xinheyun.operation.auth.dto.RoleInfoDTO
     */
    @Override
    public Optional<RoleInfoDTO> getRoleInfo(Integer roleId) {
        if (roleId == null) {
            return Optional.empty();
        }
        RoleInfoDTO roleInfoDTO = new RoleInfoDTO();
        boolean exit = queryRoleInfo(roleId, roleInfoDTO);
        if (!exit) {
            return Optional.empty();
        }
        buildPermissionList(roleId, roleInfoDTO);
        return Optional.of(roleInfoDTO);
    }

    private void buildPermissionList(Integer roleId, RoleInfoDTO roleInfoDTO) {
        List<PermissionPO> permissionPOS = permissionMapper.selectPermissionListByRoleId(roleId);//根据roleId 获取权限列表
        if (CollectionUtils.isEmpty(permissionPOS)) {
            return;
        }
        List<PermissionDTO> permissionDTOS = RolePermissionConverter.INSTANCE.permissionPOListToPermissionDTOList(permissionPOS);
        roleInfoDTO.setPermissionList(permissionDTOS);
    }

    private boolean queryRoleInfo(Integer roleId, RoleInfoDTO roleInfoDTO) {
        QueryWrapper<RolePO> wrapper = new QueryWrapper<>();
        wrapper.eq(ColumnEnum.IS_DELETE.getValue(), 0);
        wrapper.eq(ColumnEnum.ID.getValue(), roleId);
        RolePO rolePO = roleMapper.selectOne(wrapper);//根据角色id查询角色基本信息
        if (rolePO == null) {
            return false;
        }
        RoleDTO roleDTO = RolePermissionConverter.INSTANCE.rolePOToRoleDTO(rolePO);
        roleInfoDTO.setBaseInfo(roleDTO);
        return true;
    }

    /* *
     * @description: 删除角色信息，包括关联的用户，部门，权限
     * @param roleId
     * @author longzix
     * @date 2021/7/25 22:08
     * @return java.lang.Integer 返回受影响的数据条数
     */
    @Transactional
    @Override
    public Optional<Integer> deleteRole(Integer roleId) {
        if (roleId == null) {
            return Optional.empty();
        }
        Integer isAdmin = roleMapper.selectIsAdminById(roleId);
        if (isAdmin == 1) {
            return Optional.empty();
        }
        UpdateWrapper<RolePO> roleWrapper = new UpdateWrapper<>();
        roleWrapper.eq(ColumnEnum.ID.getValue(), roleId);
        roleWrapper.set(ColumnEnum.IS_DELETE.getValue(), 1);
        roleMapper.update(null, roleWrapper);
        taskExecutor.execute(() -> {
            deleteUserCacheByRoleId(roleId);//删除用户缓存
        });
        deleteRoleRefer(roleId);//根据角色Id 删除用户角色关联表信息
        return Optional.of(roleId);
    }

    /* *
     * @description: 删除角色关联信息，逻辑删除
     * @param roleId
     * @author longzix
     * @date 2021/7/26 11:51
     * @return java.lang.Integer
     */
    private void deleteRoleRefer(Integer roleId) {
        deleteRoleDepartUserRefer(roleId);
        deleteRolePermissionRefer(roleId);
    }

    private void deleteRoleDepartUserRefer(Integer roleId) {
        //根据角色Id 删除用户角色关联表信息
        UpdateWrapper<UserRolePO> updateWrapper = new UpdateWrapper<>();//UpdateWrapper加上泛型只能对应的PO类可以使用
        updateWrapper.eq(ColumnEnum.ROLE_ID.getValue(), roleId);
        updateWrapper.set(ColumnEnum.IS_DELETE.getValue(), 1);
        userRoleMapper.update(null, updateWrapper);
    }

    private void deleteRolePermissionRefer(Integer roleId) {
        //根据角色Id 删除用户角色关联表信息
        UpdateWrapper<RolePermissionPO> updateWrapper = new UpdateWrapper<>();//UpdateWrapper加上泛型只能对应的PO类可以使用
        updateWrapper.eq(ColumnEnum.ROLE_ID.getValue(), roleId);
        updateWrapper.set(ColumnEnum.IS_DELETE.getValue(), 1);
        rolePermissionMapper.update(null, updateWrapper);
    }

    /* *
     * @description: 创建角色，包括关联的用户列表，部门列表，权限列表
     * @param roleInfoEditBO
     * @author longzix
     * @date 2021/7/25 23:22
     * @return java.lang.Integer
     */
    @Transactional
    @Override
    public Optional<Integer> createRole(RoleBO roleBO) {
        if (roleBO == null) {
            return Optional.empty();
        }
        RolePO rolePO = RoleConverter.INSTANCE.RoleBOToPO(roleBO);
        log.info("RolePO", rolePO.getRoleCode(), rolePO.getRoleName(), rolePO.getRemark());
        roleMapper.insert(rolePO);//插入角色基本信息,返回自增的主键id
        return Optional.of(rolePO.getId().intValue());
    }


    /* *
     * @description: 创建角色 与用户部门关联信息
     * @param roleDepartUserEditBO
     * @author longzix
     * @date 2021/7/28 11:08
     * @return void
     */
    private void createRoleDepartUserRefer(RoleDepartUserEditBO roleDepartUserEditBO) {
        Optional<List<UserRolePO>> userRolePOS = roleDepartUserEditBO.toUserRolePOList();
        userRolePOS.ifPresent(userRolePOS1 -> userRoleService.saveBatch(userRolePOS1));
    }

    /* *
     * @description: 创建 角色与权限关联信息
     * @param rolePermissionEditBO
     * @author longzix
     * @date 2021/7/28 11:07
     * @return void
     */
    private void createRolePermissionRefer(RolePermissionEditBO rolePermissionEditBO) {
        Optional<List<RolePermissionPO>> rolePermissionPOS = rolePermissionEditBO.toRolePermissionPOList();
        rolePermissionPOS.ifPresent(rolePermissionPOS1 -> perRoleService.saveBatch(rolePermissionPOS1));
    }


    @Override
    public Optional<Integer> editRoleDepartUSer(RoleDepartUserEditBO roleDepartUserEditBO) {
        Integer roleId = roleDepartUserEditBO.getRoleId();
        taskExecutor.execute(() -> {
            deleteUserCacheByRoleId(roleId);//删除用户缓存
        });
        deleteRoleDepartUserRefer(roleId);//删除角色与部门 、用户的关联信息
        createRoleDepartUserRefer(roleDepartUserEditBO);//再创建
        taskExecutor.execute(() -> {
            deleteUserCacheByRoleId(roleId);//删除用户缓存
        });
        return Optional.of(roleId);
    }

    @Override
    public Optional<Integer> editRolePermission(RolePermissionEditBO rolePermissionEditBO) {
        Integer roleId = rolePermissionEditBO.getRoleId();
        taskExecutor.execute(() -> {
            deleteUserCacheByRoleId(roleId);//删除用户缓存
        });
        deleteRolePermissionRefer(roleId);
        createRolePermissionRefer(rolePermissionEditBO);
        taskExecutor.execute(() -> {
            deleteUserCacheByRoleId(roleId);//删除用户缓存
        });
        return Optional.of(roleId);
    }

    /* *
     * @description: 根据角色id 删除与该角色关联的所有用户的 缓存
     * @param roleId
     * @author longzix
     * @date 2021/7/28 11:53
     * @return void
     */
    private void deleteUserCacheByRoleId(Integer roleId) {
        Set<String> userIdSet = userRoleMapper.selectUserIdListByRoleId(roleId);//查询出与角色关联的所有用户
        if (CollectionUtils.isEmpty(userIdSet)) {
            return;
        }
        Set<String> fullUserIdSet = new HashSet<>();
        userIdSet.forEach(userId -> {
            fullUserIdSet.add(buildKeyOfUserPermission(userId));
        });
        log.info("删除用户缓存：{}", fullUserIdSet);
        redisTemplate.delete(fullUserIdSet);

    }

    /* *
     * @description: 修改角色的基本信息，，（名称）
     * @param roleParam
     * @author longzix
     * @date 2021/7/28 11:19
     * @return java.util.Optional<java.lang.Integer>
     */
    @Override
    public Optional<Integer> editRole(RoleParam roleParam) {
        Integer RoleId = roleParam.getId();
        if (RoleId == null) {
            return Optional.empty();
        }
        RolePO rolePO = RolePermissionConverter.INSTANCE.roleParamToRolePO(roleParam);
        rolePO.setId(RoleId.longValue());
        int updateCount = roleMapper.updateById(rolePO);
        if (updateCount == 0) {
            return Optional.empty();
        }
        return Optional.of(roleParam.getId());
    }


    /* *
     * @description: 从数据库获取用户对应的所有权限 的ID 集合
     * @param userId
     * @author longzix
     * @date 2021/7/27 16:27
     * @return java.util.Optional<java.util.List<com.xinheyun.operation.auth.dto.PermissionDTO>>
     */
    private Optional<Set<Integer>> getPermissionListFromDB(String userId) {
        Optional<Set<Integer>> optRoleIdList = getRoleIdListByUserId(userId);//获取用户对应的所有角色
        if (!optRoleIdList.isPresent()) {
            return Optional.empty();
        }
        Set<Integer> roleIdList = optRoleIdList.get();
        Set<Integer> permissionIdSet = permissionMapper.selectPermissionIdListByRoleIdList(roleIdList);//在sql中去重
        if (CollectionUtils.isEmpty(permissionIdSet)) {
            return Optional.empty();
        }
        return Optional.of(permissionIdSet);//根据角色获取权限
    }

    /* *
     * @description: 获取用户对应的所有角色
     * @param userId
     * @author longzix
     * @date 2021/7/27 16:30
     * @return java.util.Optional<java.util.List<java.lang.Integer>>
     */
    private Optional<Set<Integer>> getRoleIdListByUserId(String userId) {
        Set<Integer> roleIds = userRoleMapper.selectRoleIdListByUserId(userId); //根据用户获取角色id列表
        if (CollectionUtils.isEmpty(roleIds)) {
            return Optional.empty();
        }
        return Optional.of(roleIds);
    }



    /* *
     * @description: 将用户所拥有的权限信息放到redis中缓存
     * @param userId
     * @author longzix
     * @date 2021/7/27 20:53
     * @return void
     */
    @Override
    public void setUserPermissionToRedis(String userId) {
        Optional<Set<Integer>> optional = getPermissionListFromDB(userId);
        if (!optional.isPresent()) {
            redisTemplate.opsForValue().set(buildKeyOfUserPermission(userId),new HashSet<>(), 5, TimeUnit.MINUTES);//5分钟
        }
        Set<Integer> set = optional.get();
        log.info("将用户权限信息放入缓存,key为用户id: {} ， value ：{}", userId, set.toString());
        redisTemplate.opsForValue().set(buildKeyOfUserPermission(userId), set, 2, TimeUnit.HOURS);//两小时
    }

    /* *
     * @description: 根据用户Id获取用户权限，如果缓存中有就从缓存中拿，否则从db获取，然后再放入缓存
     * @param userId
     * @author longzix
     * @date 2021/7/27 20:00
     * @return java.util.Set<java.lang.Integer>
     */
    @Override
    public Optional<Set<Integer>> getUserPermissionList(String userId) {
        Set<Integer> permissionIds = (Set<Integer>) redisTemplate.opsForValue().get(buildKeyOfUserPermission(userId));//从缓存中获取
        if (CollectionUtils.isEmpty(permissionIds)) {
            setUserPermissionToRedis(userId);//放入缓存中
            log.info("从缓存中拿用户权限数据");
            permissionIds = (Set<Integer>) redisTemplate.opsForValue().get(buildKeyOfUserPermission(userId));//从缓存中获取
        }
        if (CollectionUtils.isEmpty(permissionIds)) {
            return Optional.empty();
        }
        return Optional.of(permissionIds);
    }


    private String buildKeyOfUserPermission(String userId) {
        return RedisKeyEnum.BOSS_UID.getValue() + userId;
    }
}
